/* eslint-disable no-unused-expressions */
/* eslint-disable camelcase */
/*
  beginPath：stroke，fill，都会从上一次 beginPath 开始绘制。
  closePath：闭合路径：不是结束路径，而是关闭路径，它会试图从当前路径的终点连一条路径到起点，让整个路径闭合起来
*/

export interface Point {
  x: number;
  y: number;
}

export type Points = [Point, Point];

export interface RectOptions {
  x: number;
  y: number;
  w: number;
  h: number;
}

const DEFAULT_OPTIONS = { COLOR: 'rgba(0,0,0,0.2)', HOLLOWOUT_COLOR: 'rgba(0,0,0,1)', R: 2, RA: Math.PI * 2, W: 2 };

export const useCanvas = () => {
  const initCanvas = (id: string) => {
    const c = document.getElementById(id) as HTMLCanvasElement;
    return c.getContext('2d');
  };

  // 绘制多边形
  const renderPolygon = (
    ctx: CanvasRenderingContext2D,
    list: Point[],
    options?: { w?: number; c?: string | CanvasGradient | CanvasPattern },
  ) => {
    if (!list.length && list.length < 2) return;

    const { w, c } = {
      w: DEFAULT_OPTIONS.W,
      c: DEFAULT_OPTIONS.COLOR,
      ...(options || {}),
    };

    ctx.beginPath();

    list.forEach((item, i) => (i === 0 ? ctx.moveTo(item.x, item.y) : ctx.lineTo(item.x, item.y)));

    ctx.closePath(); // 闭合路径（最后一条和第一条连线）
    ctx.lineWidth = w;
    ctx.strokeStyle = c;
    ctx.stroke(); // 描边
  };

  // 绘制多边形背景颜色
  const drawCtxBgColor = (ctx: CanvasRenderingContext2D, color?: string | CanvasGradient | CanvasPattern) => {
    ctx.fillStyle = color || DEFAULT_OPTIONS.COLOR;
    ctx.globalCompositeOperation = 'source-over'; // 默认值
    ctx.fill();
  };

  // 镂空多边形背景颜色
  const hollowOutRect = (ctx: CanvasRenderingContext2D, color?: string | CanvasGradient | CanvasPattern) => {
    ctx.fillStyle = color || DEFAULT_OPTIONS.HOLLOWOUT_COLOR;
    ctx.globalCompositeOperation = 'destination-out'; // 镂空

    ctx.fill();
  };

  // 设置默认值
  const setGlobalCompositeOperation = (ctx: CanvasRenderingContext2D, isHollowOut?: boolean) => {
    ctx.globalCompositeOperation = isHollowOut ? 'destination-out' : 'source-over'; // 默认值
  };

  // 绘制矩形
  const renderRect = (
    ctx: CanvasRenderingContext2D,
    data: RectOptions,
    options?: { color?: string | CanvasGradient | CanvasPattern },
  ) => {
    const { x, y, w, h } = data;
    ctx.fillStyle = options?.color || DEFAULT_OPTIONS.COLOR;
    ctx.fillRect(x, y, w, h);
  };

  // 绘制点
  const renderPoint = (ctx: CanvasRenderingContext2D, point: Point, options?: { r?: number; color?: string }) => {
    const { x, y } = point;
    const { r, color } = { r: DEFAULT_OPTIONS.R, color: DEFAULT_OPTIONS.COLOR, ...(options || {}) };
    ctx.beginPath();
    ctx.fillStyle = color;
    ctx.arc(x, y, r, 0, DEFAULT_OPTIONS.RA);
    ctx.fill(); // 描点
  };

  // 绘制线
  const renderLine = (
    ctx: CanvasRenderingContext2D,
    points: Points,
    options?: { w?: number; c?: string | CanvasGradient | CanvasPattern },
  ) => {
    const [{ x: sx, y: sy }, { x: ex, y: ey }] = points;
    const { w, c } = { w: DEFAULT_OPTIONS.W, c: DEFAULT_OPTIONS.COLOR, ...(options || {}) };
    ctx.beginPath();
    ctx.moveTo(sx, sy);
    ctx.lineTo(ex, ey);
    ctx.lineWidth = w;
    ctx.strokeStyle = c;
    ctx.stroke(); // 描边
    ctx.closePath();
  };

  // 清除画布
  const clearCanvasRect = (ctx: CanvasRenderingContext2D, data: RectOptions) => {
    return new Promise((resolve) => {
      const { x, y, w, h } = data;
      ctx.clearRect(x, y, w, h);
      ctx.beginPath();
      resolve(true);
    });
  };

  // 获取两条交点 https://www.jb51.net/article/90104.htm
  const segmentsIntr = (s: [Point, Point], e: [Point, Point]) => {
    const [a, b] = s;
    const [c, d] = e;

    // 三角形abc 面积的2倍
    const area_abc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);

    // 三角形abd 面积的2倍
    const area_abd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x);

    // 面积符号相同则两点在线段同侧,不相交 (对点在线段上的情况,本例当作不相交处理);
    if (area_abc * area_abd >= 0) {
      return false;
    }

    // 三角形cda 面积的2倍
    const area_cda = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x);
    // 三角形cdb 面积的2倍
    // 注意: 这里有一个小优化.不需要再用公式计算面积,而是通过已知的三个面积加减得出.
    const area_cdb = area_cda + area_abc - area_abd;
    if (area_cda * area_cdb >= 0) {
      return false;
    }

    // 计算交点坐标
    const t = area_cda / (area_abd - area_abc);
    const dx = t * (b.x - a.x);
    const dy = t * (b.y - a.y);
    return { x: a.x + dx, y: a.y + dy };
  };

  // 判断是否相交 -- 常规的数据(注意最后一点和第一点的连线加没有)
  const isIntersect = (p: Point, list: Point[]) => {
    if (list?.length < 2) return false;

    let bool = false;
    const p2Data = list[list.length - 1];
    const p2 = { x: p2Data.x, y: p2Data.y };

    for (let i = 0; i < list.length - 1; i++) {
      const [p3Data, p4Data] = [list[i], list[i + 1]];

      const p3 = { x: p3Data.x, y: p3Data.y };
      const p4 = { x: p4Data.x, y: p4Data.y };
      const data = segmentsIntr([p, p2], [p3, p4]); // 四个点 两条线段比

      if (data) {
        bool = true;
        break;
      }
    }

    return bool;
  };

  return {
    initCanvas,
    renderRect,
    renderPoint,
    renderLine,
    renderPolygon,
    segmentsIntr,
    clearCanvasRect,
    drawCtxBgColor,
    hollowOutRect,
    isIntersect,
    setGlobalCompositeOperation,
  };
};
